﻿using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace RomanNumerals
{
    public class Kata
    {
        [Fact]
        public void One()
        {
            const Int32 number = 1;
            var converted = Convert(number);
            Assert.Equal("I", converted);
        }

        [Fact]
        public void Two()
        {
            const Int32 number = 2;
            var converted = Convert(number);
            Assert.Equal("II", converted);
        }

        [Fact]
        public void Four()
        {
            const Int32 number = 4;
            var converted = Convert(number);
            Assert.Equal("IV", converted);
        }

        [Fact]
        public void Five()
        {
            const Int32 number = 5;
            var converted = Convert(number);
            Assert.Equal("V", converted);
        }

        [Fact]
        public void Six()
        {
            const Int32 number = 6;
            var converted = Convert(number);
            Assert.Equal("VI", converted);
        }

        [Fact]
        public void Nine()
        {
            const Int32 number = 9;
            var converted = Convert(number);
            Assert.Equal("IX", converted);
        }

        [Fact]
        public void Ten()
        {
            const Int32 number = 10;
            var converted = Convert(number);
            Assert.Equal("X", converted);
        }

        [Fact]
        public void Eleven()
        {
            const Int32 number = 11;
            var converted = Convert(number);
            Assert.Equal("XI", converted);
        }

        [Fact]
        public void Fourteen()
        {
            const Int32 number = 14;
            var converted = Convert(number);
            Assert.Equal("XIV", converted);
        }

        [Fact]
        public void Twenty()
        {
            const Int32 number = 20;
            var converted = Convert(number);
            Assert.Equal("XX", converted);
        }

        [Fact]
        public void TwentyFour()
        {
            const Int32 number = 24;
            var converted = Convert(number);
            Assert.Equal("XXIV", converted);
        }

        [Fact]
        public void Fourty()
        {
            const Int32 number = 40;
            var converted = Convert(number);
            Assert.Equal("XL", converted);
        }

        [Fact]
        public void FourtyNine()
        {
            const Int32 number = 49;
            var converted = Convert(number);
            Assert.Equal("XLIX", converted);
        }

        [Fact]
        public void Fifty()
        {
            const Int32 number = 50;
            var converted = Convert(number);
            Assert.Equal("L", converted);
        }

        [Fact]
        public void Ninety()
        {
            const Int32 number = 90;
            var converted = Convert(number);
            Assert.Equal("XC", converted);
        }

        [Fact]
        public void NinetyNine()
        {
            const Int32 number = 99;
            var converted = Convert(number);
            Assert.Equal("XCIX", converted);
        }

        [Fact]
        public void OneHundred()
        {
            const Int32 number = 100;
            var converted = Convert(number);
            Assert.Equal("C", converted);
        }

        [Fact]
        public void NinetyFour()
        {
            const Int32 number = 94;
            var converted = Convert(number);
            Assert.Equal("XCIV", converted);
        }

        [Fact]
        public void OneHundredOne()
        {
            const Int32 number = 101;
            var converted = Convert(number);
            Assert.Equal("CI", converted);
        }

        [Fact]
        public void OneHundredFour()
        {
            const Int32 number = 104;
            var converted = Convert(number);
            Assert.Equal("CIV", converted);
        }

        [Fact]
        public void OneHundredNine()
        {
            const Int32 number = 109;
            var converted = Convert(number);
            Assert.Equal("CIX", converted);
        }

        [Fact]
        public void OneHundredTen()
        {
            const Int32 number = 110;
            var converted = Convert(number);
            Assert.Equal("CX", converted);
        }

        [Fact]
        public void ThreeHundredNinetyNine()
        {
            const Int32 number = 399;
            var converted = Convert(number);
            Assert.Equal("CCCXCIX", converted);
        }

        [Fact]
        public void ThreeHundredFourtyNine()
        {
            const Int32 number = 349;
            var converted = Convert(number);
            Assert.Equal("CCCXLIX", converted);
        }

        [Fact]
        public void FiveHundred()
        {
            const Int32 number = 500;
            var converted = Convert(number);
            Assert.Equal("D", converted);
        }

        [Fact]
        public void FourHundred()
        {
            const Int32 number = 400;
            var converted = Convert(number);
            Assert.Equal("CD", converted);
        }

        [Fact]
        public void FourHundredFourtyNine()
        {
            const Int32 number = 449;
            var converted = Convert(number);
            Assert.Equal("CDXLIX", converted);
        }

        [Fact]
        public void FourHundredFifty()
        {
            const Int32 number = 450;
            var converted = Convert(number);
            Assert.Equal("CDL", converted);
        }

        [Fact]
        public void FourHundredNinetyNine()
        {
            const Int32 number = 499;
            var converted = Convert(number);
            Assert.Equal("CDXCIX", converted);
        }

        [Fact]
        public void NineHundredNinetyNine()
        {
            const Int32 number = 999;
            var converted = Convert(number);
            Assert.Equal("CMXCIX", converted);
        }

        [Fact]
        public void TwoThousandNineHundredNinetyNine()
        {
            const Int32 number = 2999;
            var converted = Convert(number);
            Assert.Equal("MMCMXCIX", converted);
        }

        [Fact]
        public void ThreeThousand()
        {
            const Int32 number = 3000;
            var converted = Convert(number);
            Assert.Equal("MMM", converted);
        }

        [Fact]
        public void TwoThousandTwoHundredTwentyTwo()
        {
            const Int32 number = 2944;
            var converted = Convert(number);
            Assert.Equal("MMCMXLIV", converted);
        }

        static readonly LinkedList<Tuple<Int32, Char[]>> Symbols = new LinkedList<Tuple<Int32, Char[]>>(new[]
                                                                                                    {
                                                                                                        new Tuple<Int32, Char[]>(1000, new[] {'M'}),
                                                                                                        new Tuple<Int32, Char[]>(900, new[] {'C', 'M'}),
                                                                                                        new Tuple<Int32, Char[]>(500, new[] {'D'}),
                                                                                                        new Tuple<Int32, Char[]>(400, new[] {'C', 'D'}),
                                                                                                        new Tuple<Int32, Char[]>(100, new[] {'C'}),
                                                                                                        new Tuple<Int32, Char[]>(90, new[] {'X', 'C'}),
                                                                                                        new Tuple<Int32, Char[]>(50, new[] {'L'}),
                                                                                                        new Tuple<Int32, Char[]>(40, new[] {'X', 'L'}),
                                                                                                        new Tuple<Int32, Char[]>(10, new[] {'X'}),
                                                                                                        new Tuple<Int32, Char[]>(9, new[] {'I', 'X'}),
                                                                                                        new Tuple<Int32, Char[]>(5, new[] {'V'}),
                                                                                                        new Tuple<Int32, Char[]>(4, new[] {'I', 'V'}),
                                                                                                        new Tuple<Int32, Char[]>(1, new[] {'I'}) 
                                                                                                    });

        static String Convert(Int32 input, StringBuilder source, LinkedListNode<Tuple<Int32, Char[]>> node)
        {
            if (node == null)
                return source.ToString();

            for (var i = 0; i < input.DivideBy(node.Value.Item1); i++)
                source.Append(node.Value.Item2);

            return Convert(input.ModuleBy(node.Value.Item1), source, node.Next);
        }

        static String Convert(Int32 input)
        {
            return Convert(input, new StringBuilder(), Symbols.First);
        }
    }

    public static class Int32Extensions
    {
        public static Int32 DivideBy(this Int32 source, Int32 divisor)
        {
            return source/divisor;
        }

        public static Int32 ModuleBy(this Int32 source, Int32 divisor)
        {
            return source % divisor;
        }
    }
}